//
// Created by Criinal Nicole on 2021/4/26.
//

#include "Graph.h"
#include <string>
#include <iostream>
#include <fstream>

Edge::Edge(const Edge &_edge) {
    _from = _edge._from;
    _to = _edge._to;
    _label = _edge._label;
    _edgeState = _edge._edgeState;
}

Edge::Edge(int from,
           int to,
           int label,
           EdgeState edgeState) {
    _from = from;
    _to = to;
    _label = label;
    _edgeState = edgeState;
}

int Edge::getFromVertex() const {
    return _from;
}

int Edge::getToVertex() const {
    return _to;
}

int Edge::getLabel() const {
    return _label;
}

bool Edge::labelEqual(const Edge &b) const {
    return _label == b.getLabel();
}

bool Edge::labelEqual(int queryLabel) const {
    return _label == queryLabel;
}

EdgeState Edge::getEdgeState() const {
    return _edgeState;
}

void Edge::changeEdgeState(EdgeState edgeState) {
    _edgeState = edgeState;
}

void Edge::update(std::map<int, int> &vertexMap) {
    _from = vertexMap[_from];
    _to = vertexMap[_to];
}

Vertex::Vertex(int id, int label) {
    _id = id;
    _label = label;
}

int Vertex::getId() const {
    return _id;
}

int Vertex::getLabel() const {
    return _label;
}

std::vector<Edge> Vertex::findInEdge(int queryLabel) {
    std::vector<Edge> retEdges{};
    for (const auto &edge: inEdges) {
        if (edge.labelEqual(queryLabel))retEdges.push_back(edge);
    }
    return retEdges;
}

std::vector<Edge> Vertex::findOutEdge(int queryLabel) {
    std::vector<Edge> retEdges{};
    for (const auto &edge: outEdges) {
        if (edge.labelEqual(queryLabel))retEdges.push_back(edge);
    }
    return retEdges;
}

int Vertex::getInDegree() const {
    return inEdges.size();
}

int Vertex::getOutDegree() const {
    return outEdges.size();
}

bool Vertex::labelEqual(const Vertex &vertex) const {
    return _label == vertex.getLabel();
}

bool Vertex::labelEqual(int queryLabel) const {
    return _label == queryLabel;
}

Vertex::Vertex(const Vertex &vertex) : PropertySet(vertex.PropertySet) {
    _id = vertex._id;
    _label = vertex._label;
    for (const auto &inEdge: vertex.inEdges) {
        inEdges.emplace_back(inEdge);
    }
    for (const auto &outEdge:vertex.outEdges) {
        outEdges.emplace_back(outEdge);
    }
}

void Vertex::updateEdges(std::map<int, int> &vertexMap) {
    for (auto &edge :inEdges)edge.update(vertexMap);
    for (auto &edge :outEdges)edge.update(vertexMap);
}

bool operator<(const Vertex &a, const Vertex &b) {
    return a._id < b._id;
}

Graph::Graph(int id) { _id = id; }

Graph::Graph(std::string dataFileName) {
    clear();
    std::ifstream vertexIFStream(dataFileName + ".v");
    if (!vertexIFStream.is_open()) {
        std::cout << "Can't read file: "
                  << dataFileName + ".v"
                  << std::endl;
    }
    int vertexNum{0};
//    vertexIFStream >> vertexNum;
    int v, vLabel;

    while (vertexIFStream >> v >> vLabel) {

        Vertex tmpVertex(v, vLabel);
        int propertyNum;
        vertexIFStream >> propertyNum;
        for (int j = 0; j < propertyNum; ++j) {
            int propertyName;
            auto *propertyValue = new int;
            vertexIFStream >> propertyName >> (*propertyValue);
            PropertyValue _propertyValue;
            _propertyValue._propertyValueType = INT;
            _propertyValue._propertyValuePointer = propertyValue;
            tmpVertex.PropertySet.insert({propertyName, _propertyValue});
        }
        push_back(tmpVertex);
    }
    vertexIFStream.close();

//    按ID排序
    std::sort(begin(),
              end(),
              [](const Vertex &a,const Vertex &b) {
                  return a.getId() < b.getId();
              });
    std::map<int, int> map;
    for (int i = 0; i < size(); ++i) {
        map[at(i).getId()] = i;
        at(i)._id = i;
    }
    std::ifstream edgeIFStream(dataFileName + ".e");
    if (!edgeIFStream.is_open()) {
        std::cout << "Can't read file: "
                  << dataFileName + ".e"
                  << std::endl;
    }
    int from, to, eLabel;
    while (edgeIFStream >> from >> to >> eLabel) {
        makeEdge(at(map[from]), at(map[to]), eLabel);
    }
    edgeIFStream.close();
}


/*
 * 数据格式
 * 点数
 * 点ID 点标签
 * 属性数量
 * 属性名 属性值
 * ……
 * 边数
 * 起点 终点
 * ……
 */
void Graph::fileOut(std::ofstream &fOut) {
    fOut << size() << std::endl;
    int edgeSize = 0;
    for (const auto &vertex:*this) {
        edgeSize += vertex.inEdges.size();
        fOut << vertex.getId() << " " << vertex.getLabel() << std::endl;
        fOut << vertex.PropertySet.size() << std::endl;
        for (const auto &property:vertex.PropertySet) {
            fOut << property._propertyName << " "
                 << property._propertyValue << std::endl;
        }
    }
    fOut << edgeSize << std::endl;
    for (const auto &vertex:*this) {
        for (const auto &edge: vertex.inEdges) {
            fOut << edge.getFromVertex() << " "
                 << edge.getToVertex() << " "
                 << edge.getLabel() << std::endl;
        }
    }
}

void Graph::makeEdge(Vertex &from, Vertex &to, int label) {
    from.outEdges.emplace_back(from.getId(), to.getId(), label, EdgeState::out);
    to.inEdges.emplace_back(from.getId(), to.getId(), label, EdgeState::in);
}

void Graph::updateVertexLabelInfo(std::set<int> &vertexLabelSet) const {
    vertexLabelSet.clear();
    for (auto &vertex:(*this)) {
        vertexLabelSet.insert(vertex.getLabel());
    }
}

void Graph::updateEdgeLabelInfo(std::set<EdgeModel> &edgeLabelSet) const {
    edgeLabelSet.clear();
    for (auto &vertex:(*this)) {
        for (auto &edge: vertex.inEdges) {
            EdgeModel tmpEdge(at(edge.getFromVertex()).getLabel(),
                              at(edge.getToVertex()).getLabel(),
                              edge.getLabel(),
                              undefined);
            edgeLabelSet.insert(tmpEdge);
        }
    }
}

void Graph::updatePropertySetInfo(std::map<std::pair<int, int>,
        std::set<PropertyValue>> &propertySet) const {
    propertySet.clear();
    for (auto &vertex:(*this)) {
        for (auto &property: vertex.PropertySet) {
            propertySet[std::make_pair(vertex.getLabel(), property._propertyName)].insert(property._propertyValue);
        }
    }
}

Graph::Graph(const Graph &graph) : vector(graph) {
    _id = graph._id;
}

int Graph::countEdge() {
    int retEdgeSize{0};
    for (auto &vec:*this) {
        retEdgeSize += vec.inEdges.size();
    }
    return retEdgeSize;
}

bool operator<(Property a, Property b) {
    return std::make_pair(a._propertyName, a._propertyValue)
           < std::make_pair(b._propertyName, b._propertyValue);
}

Property::Property(int propertyName, PropertyValue propertyValue) {
    _propertyName = propertyName;
    _propertyValue = propertyValue;
}
